﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using YArchitech.ProgressBar;
using YArchitech.Revit;
using YJKRevitView;
using YJKInspectModel.ModelValidation.Commands;
using YJKInspectModel.ModelValidation.LongitudinalModel;
using YJKInspectModel.ModelValidation.Utils;

namespace YJKInspectModel.LongitudinalModel
{
	public class LongitudinalModelCheckingController
	{
		public LongitudinalModelCheckingController(ExternalCommandData cmdData)
		{
			this.m_uiApp = cmdData.Application;
			this.m_uiDoc = this.m_uiApp.ActiveUIDocument;
			this.m_app = this.m_uiApp.Application;
			this.m_doc = this.m_uiDoc.Document;
		}

		public Result Checking(ref string message)
		{
			MainForm mainForm = new MainForm();
			if (mainForm.ShowDialog() != DialogResult.OK)
			{
				return Autodesk.Revit.UI.Result.Cancelled;
			}
			this.m_CheckingTypeStatesInfo = mainForm.GetCheckingTypeStatesInfo;
			if (!this.GetModelElements(mainForm.IsByCurrentFloorSelect))
			{
				return Autodesk.Revit.UI.Result.Cancelled;
			}
			if (!this.CheckingByCategory())
			{
				return Autodesk.Revit.UI.Result.Failed;
			}
			MessageBox.Show("模型扣减完成", "盈建科提示");
			return Autodesk.Revit.UI.Result.Succeeded;
		}

		private bool GetModelElements(bool isByCurrentFloorSelect)
		{
			if (isByCurrentFloorSelect)
			{
				if (!this.GetModelsByUserSelect())
				{
					return false;
				}
			}
			else
			{
				List<Level> list = new List<Level>();
				if (!FloorSelectManager.SelectFloorLevels(this.m_doc, ref list))
				{
					return false;
				}
				list.Sort(new LevelComparer());
				if (!this.GetModelsByLevel(list))
				{
					return false;
				}
			}
			return true;
		}

		private bool CheckingByCategory()
		{
			ProgressBarForm progressBarForm = new ProgressBarForm(10, ProgressBarStyle.Blocks, true);
			progressBarForm.Show(CmdLongitudinalModelChecking._hWndRevit);
			this.DeductWithAutoJoined(this.m_Beams, DeductionRelationshipPriority.CPL_Beam);
			progressBarForm.RefreshProgressBar();
			this.DeductWithAutoJoined(this.m_StructColumns, DeductionRelationshipPriority.CPL_StructColumn);
			progressBarForm.RefreshProgressBar();
			this.DeductWithAutoJoined(this.m_StructWalls, DeductionRelationshipPriority.CPL_StructWall);
			progressBarForm.RefreshProgressBar();
			this.DeductWithAutoJoined(this.m_ArchColumns, DeductionRelationshipPriority.CPL_ArchColumn);
			progressBarForm.RefreshProgressBar();
			this.DeductWithNotJoined(this.m_Beams, DeductionRelationshipPriority.CPL_Beam);
			progressBarForm.RefreshProgressBar();
			this.DeductWithNotJoined(this.m_StructColumns, DeductionRelationshipPriority.CPL_StructColumn);
			progressBarForm.RefreshProgressBar();
			this.DeductWithNotJoined(this.m_StructWalls, DeductionRelationshipPriority.CPL_StructWall);
			progressBarForm.RefreshProgressBar();
			this.DeductWithNotJoined(this.m_Floors, DeductionRelationshipPriority.CPL_Floor);
			progressBarForm.RefreshProgressBar();
			this.DeductWithNotJoined(this.m_ArchColumns, DeductionRelationshipPriority.CPL_ArchColumn);
			progressBarForm.RefreshProgressBar();
			this.DeductWithNotJoined(this.m_ArchWalls, DeductionRelationshipPriority.CPL_ArchWall);
			progressBarForm.RefreshProgressBar();
			return true;
		}

		private bool DeductWithAutoJoined(List<Element> elements, DeductionRelationshipPriority priorityLevel)
		{
			Transaction transaction = null;
			try
			{
				transaction = new Transaction(this.m_doc);
				transaction.Start("join elemet");
				foreach (Element element in elements)
				{
					foreach (ElementId elementId in JoinGeometryUtils.GetJoinedElements(this.m_doc, element))
					{
						Element element2 = this.m_doc.GetElement(elementId);
						DeductionRelationshipPriority secondLevel = DeductionRelationshipPriority.CPL_Invalid;
						if (this.GetPriorityLevel(element2, ref secondLevel))
						{
							this.DeductElementPair(element, priorityLevel, element2, secondLevel);
						}
					}
				}
				transaction.Commit();
			}
			catch (Exception ex)
			{
				string message = ex.Message;
				if (transaction != null && transaction.HasStarted())
				{
					transaction.RollBack();
				}
			}
			return true;
		}

		private bool DeductWithNotJoined(List<Element> elements, DeductionRelationshipPriority priorityLevel)
		{
			Transaction transaction = null;
			try
			{
				transaction = new Transaction(this.m_doc);
				transaction.Start("join elemet");
				foreach (Element element in elements)
				{
					FilteredElementCollector filteredElementCollector = new FilteredElementCollector(this.m_doc);
					ElementIntersectsElementFilter elementIntersectsElementFilter = new ElementIntersectsElementFilter(element, false);
					foreach (Element element2 in filteredElementCollector.WherePasses(elementIntersectsElementFilter).ToElements())
					{
						DeductionRelationshipPriority secondLevel = DeductionRelationshipPriority.CPL_Invalid;
						if (this.GetPriorityLevel(element2, ref secondLevel))
						{
							this.DeductElementPair(element, priorityLevel, element2, secondLevel);
						}
					}
				}
				transaction.Commit();
			}
			catch (Exception ex)
			{
				string message = ex.Message;
				if (transaction != null && transaction.HasStarted())
				{
					transaction.RollBack();
				}
			}
			return true;
		}

		private bool GetPriorityLevel(Element element, ref DeductionRelationshipPriority priorityLevel)
		{
			int integerValue = element.Category.Id.IntegerValue;
			if (integerValue <= -2001320)
			{
				if (integerValue != -2001330)
				{
					if (integerValue == -2001320)
					{
						priorityLevel = DeductionRelationshipPriority.CPL_Beam;
					}
				}
				else
				{
					priorityLevel = DeductionRelationshipPriority.CPL_StructColumn;
				}
			}
			else if (integerValue != -2000100)
			{
				if (integerValue != -2000032)
				{
					if (integerValue == -2000011)
					{
                        if ((int)(element as Wall).StructuralUsage == 1)
						{
							priorityLevel = DeductionRelationshipPriority.CPL_StructWall;
						}
						else
						{
							priorityLevel = DeductionRelationshipPriority.CPL_ArchWall;
						}
					}
				}
				else
				{
					priorityLevel = DeductionRelationshipPriority.CPL_Floor;
				}
			}
			else
			{
				priorityLevel = DeductionRelationshipPriority.CPL_ArchColumn;
			}
			return priorityLevel != DeductionRelationshipPriority.CPL_Invalid;
		}

		private void DeductElementPair(Element first, DeductionRelationshipPriority firstLevel, Element second, DeductionRelationshipPriority secondLevel)
		{
			CheckingOrder checkingOrder = this.IncludeCheckingType(firstLevel, secondLevel);
			if (checkingOrder == CheckingOrder.CO_NoCheck)
			{
				return;
			}
			string strIdPair = first.Id.ToString() + second.Id.ToString();
			if (this.m_OperatedElementIdPairList.FindIndex((string item) => item.CompareTo(strIdPair) == 0) != -1)
			{
				return;
			}
			this.m_OperatedElementIdPairList.Add(second.Id.ToString() + first.Id.ToString());
			if (firstLevel > secondLevel)
			{
				if (checkingOrder == CheckingOrder.CO_FirstToSecond)
				{
					this.SetJoinOrder(first, second);
					return;
				}
				this.SetJoinOrder(second, first);
				return;
			}
			else
			{
				if (checkingOrder == CheckingOrder.CO_FirstToSecond)
				{
					this.SetJoinOrder(second, first);
					return;
				}
				this.SetJoinOrder(first, second);
				return;
			}
		}

		private CheckingOrder IncludeCheckingType(DeductionRelationshipPriority firstLevel, DeductionRelationshipPriority secondLevel)
		{
			return this.m_CheckingTypeStatesInfo.IncludeCheckingType(firstLevel, secondLevel);
		}

		private void SetJoinOrder(Element elementFirst, Element elementSecond)
		{
			if (JoinGeometryUtils.AreElementsJoined(this.m_doc, elementFirst, elementSecond))
			{
				if (JoinGeometryUtils.IsCuttingElementInJoin(this.m_doc, elementFirst, elementSecond))
				{
					return;
				}
				SubTransaction subTransaction = new SubTransaction(this.m_doc);
				try
				{
					subTransaction.Start();
					JoinGeometryUtils.SwitchJoinOrder(this.m_doc, elementFirst, elementSecond);
					subTransaction.Commit();
					return;
				}
				catch (Exception)
				{
					subTransaction.RollBack();
					return;
				}
			}
			SubTransaction subTransaction2 = new SubTransaction(this.m_doc);
			try
			{
				subTransaction2.Start();
				JoinGeometryUtils.JoinGeometry(this.m_doc, elementFirst, elementSecond);
				subTransaction2.Commit();
			}
			catch (Exception)
			{
				subTransaction2.RollBack();
				return;
			}
			if (!JoinGeometryUtils.IsCuttingElementInJoin(this.m_doc, elementFirst, elementSecond))
			{
				SubTransaction subTransaction3 = new SubTransaction(this.m_doc);
				try
				{
					subTransaction3.Start();
					JoinGeometryUtils.SwitchJoinOrder(this.m_doc, elementFirst, elementSecond);
					subTransaction3.Commit();
				}
				catch (Exception)
				{
					subTransaction3.RollBack();
				}
			}
		}

		private bool GetModelsByLevel(List<Level> levelList)
		{
			List<KeyValuePair<double, double>> list = new List<KeyValuePair<double, double>>();
			for (int i = 0; i < levelList.Count; i++)
			{
				double elevation = levelList[i].Elevation;
				double value;
				if (i == levelList.Count - 1)
				{
					value = elevation + Common.MMToFeet(3000.0);
				}
				else
				{
					value = levelList[i + 1].Elevation;
				}
				list.Add(new KeyValuePair<double, double>(elevation, value));
			}
			this.GetBeams(levelList);
			this.GetColumns(list, levelList, true);
			this.GetFloors(levelList);
			this.GetColumns(list, levelList, false);
			this.GetWalls(list, levelList);
			return true;
		}

		private void GetBeams(List<Level> levelList)
		{
			foreach (Element element in new FilteredElementCollector(this.m_doc).OfCategory(BuiltInCategory.OST_StructuralFraming).OfClass(typeof(FamilyInstance)).ToElements())
			{
				ElementId elementId = element.get_Parameter(BuiltInParameter.INSTANCE_REFERENCE_LEVEL_PARAM).AsElementId();
				Level beamLevel = this.m_doc.GetElement(elementId) as Level;
				if (beamLevel != null && levelList.FindIndex((Level item) => Geometry.IsEqual(item.Elevation, beamLevel.Elevation)) != -1)
				{
					this.m_Beams.Add(element);
				}
			}
		}

		private void GetFloors(List<Level> levelList)
		{
			foreach (Element element in new FilteredElementCollector(this.m_doc).OfClass(typeof(Floor)).ToElements())
			{
				Floor elem = element as Floor;
				Level floorLevel = elem.GetYJKLevel();
				if (floorLevel != null && levelList.FindIndex((Level item) => Geometry.IsEqual(item.Elevation, floorLevel.Elevation)) != -1)
				{
					this.m_Floors.Add(element);
				}
			}
		}

		private void GetColumns(List<KeyValuePair<double, double>> floorElevationPairList, List<Level> levelList, bool isStructColumn)
		{
			BuiltInCategory builtInCategory = BuiltInCategory.OST_Columns;
			if (isStructColumn)
			{
				builtInCategory = BuiltInCategory.OST_StructuralColumns;
			}
			foreach (Element element in new FilteredElementCollector(this.m_doc).OfCategory(builtInCategory).OfClass(typeof(FamilyInstance)).ToElements())
			{
				double min = 0.0;
				double max = 0.0;
				this.GetColumnElevations(element, ref min, ref max);
				int i = 0;
				while (i < floorElevationPairList.Count)
				{
					KeyValuePair<double, double> keyValuePair = floorElevationPairList[i];
					if (this.IsOverlap(min, max, keyValuePair.Key, keyValuePair.Value))
					{
						if (isStructColumn)
						{
							this.m_StructColumns.Add(element);
							break;
						}
						this.m_ArchColumns.Add(element);
						break;
					}
					else
					{
						i++;
					}
				}
			}
		}

		private void GetColumnElevations(Element column, ref double bottom, ref double top)
		{
			Parameter parameter = column.get_Parameter(BuiltInParameter.FAMILY_BASE_LEVEL_PARAM);
			if (parameter != null)
			{
				ElementId elementId = parameter.AsElementId();
				Level level = this.m_doc.GetElement(elementId) as Level;
				bottom = level.Elevation;
			}
			Parameter parameter2 = column.get_Parameter(BuiltInParameter.FAMILY_BASE_LEVEL_OFFSET_PARAM);
			if (parameter2 != null)
			{
				double num = parameter2.AsDouble();
				bottom += num;
			}
			Parameter parameter3 = column.get_Parameter(BuiltInParameter.FAMILY_TOP_LEVEL_PARAM);
			if (parameter3 != null)
			{
				ElementId elementId2 = parameter3.AsElementId();
				Level level2 = this.m_doc.GetElement(elementId2) as Level;
				top = level2.Elevation;
			}
			Parameter parameter4 = column.get_Parameter(BuiltInParameter.SCHEDULE_TOP_LEVEL_OFFSET_PARAM);
			if (parameter4 != null)
			{
				double num2 = parameter4.AsDouble();
				top += num2;
			}
		}

		private void GetWalls(List<KeyValuePair<double, double>> floorElevationPairList, List<Level> levelList)
		{
			foreach (Element element in new FilteredElementCollector(this.m_doc).OfClass(typeof(Wall)).ToElements())
			{
				Wall wall = element as Wall;
				double min = 0.0;
				double max = 0.0;
				this.GetWallElevations(wall, ref min, ref max);
				for (int i = 0; i < floorElevationPairList.Count; i++)
				{
					KeyValuePair<double, double> keyValuePair = floorElevationPairList[i];
					if (this.IsOverlap(min, max, keyValuePair.Key, keyValuePair.Value))
					{
                        if ((int)wall.StructuralUsage == 1)
						{
							this.m_StructWalls.Add(element);
						}
						else
						{
							this.m_ArchWalls.Add(element);
						}
					}
				}
			}
		}

		private void GetWallElevations(Wall wall, ref double bottom, ref double top)
		{
			ElementId elementId = wall.get_Parameter(BuiltInParameter.WALL_BASE_CONSTRAINT).AsElementId();
			Level level = this.m_doc.GetElement(elementId) as Level;
			bottom = level.Elevation;
			double num = wall.get_Parameter(BuiltInParameter.WALL_BASE_OFFSET).AsDouble();
			bottom += num;
			ElementId elementId2 = wall.get_Parameter(BuiltInParameter.WALL_HEIGHT_TYPE).AsElementId();
			if (elementId2 == ElementId.InvalidElementId)
			{
				double num2 = wall.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM).AsDouble();
				top = bottom + num2;
				return;
			}
			Level level2 = this.m_doc.GetElement(elementId2) as Level;
			top = level2.Elevation;
			double num3 = wall.get_Parameter(BuiltInParameter.WALL_TOP_OFFSET).AsDouble();
			top += num3;
		}

		private bool IsOverlap(double min1, double max1, double min2, double max2)
		{
			return !Geometry.Greaterthan_Or_Equal(min2, max1) && !Geometry.Lessthan_Or_Equal(max2, min1);
		}

		private bool GetModelsByUserSelect()
		{
			IList<Reference> list = null;
			Selection selection = this.m_uiDoc.Selection;
			try
			{
				list = selection.PickObjects((Autodesk.Revit.UI.Selection.ObjectType)1, new ModelsFilter(this.m_doc), "选择进行检查的实体");
			}
			catch (Exception)
			{
				return false;
			}
			foreach (Reference reference in list)
			{
				Element element = this.m_doc.GetElement(reference);
				if (element.GetType() == typeof(Wall))
				{
                    if ((int)(element as Wall).StructuralUsage == 1)
					{
						this.m_StructWalls.Add(element);
					}
					else
					{
						this.m_ArchWalls.Add(element);
					}
				}
				else if (element.GetType() == typeof(Floor))
				{
					this.m_Floors.Add(element);
				}
				else if (element.Category.Id.IntegerValue == -2001320)
				{
					this.m_Beams.Add(element);
				}
				else if (element.Category.Id.IntegerValue == -2001330)
				{
					this.m_StructColumns.Add(element);
				}
				else if (element.Category.Id.IntegerValue == -2000100)
				{
					this.m_ArchColumns.Add(element);
				}
			}
			return true;
		}

		private UIApplication m_uiApp;

		private UIDocument m_uiDoc;

        private Autodesk.Revit.ApplicationServices.Application m_app;

		private Document m_doc;

		private CheckingTypeStates m_CheckingTypeStatesInfo = new CheckingTypeStates();

		private List<string> m_OperatedElementIdPairList = new List<string>();

		private List<Element> m_Beams = new List<Element>();

		private List<Element> m_StructColumns = new List<Element>();

		private List<Element> m_StructWalls = new List<Element>();

		private List<Element> m_Floors = new List<Element>();

		private List<Element> m_ArchColumns = new List<Element>();

		private List<Element> m_ArchWalls = new List<Element>();
	}
}
